package com.game.logic.core.ranking;

import com.coment.enums.RankingType;
import lombok.AllArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

@AllArgsConstructor
public class RakingService<T> {
    private RedisTemplate<String, T> redisTemplate;

    public void addLine(String tag, RankingType rankingType, T t, double value) {
        String key = "ranking:%s:%s".formatted(tag, rankingType.getValue());
        this.redisTemplate.opsForZSet().add(key, t, value);
    }

    public Set<ZSetOperations.TypedTuple<T>> reverseRangeWithScores(String tag, RankingType rankingType) {
        long start = 0L;
        long end = -1L;
        return reverseRangeWithScores(tag, rankingType, start, end);
    }

    public Set<ZSetOperations.TypedTuple<T>> reverseRangeWithScores(
            String tag,
            RankingType rankingType,
            long start,
            long end
    ) {
        String key = "ranking:%s:%s".formatted(tag, rankingType.getValue());
        return this.redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
    }

    public Set<T> reverseRange(
            String tag,
            RankingType rankingType,
            long start,
            long end
    ) {
        String key = "ranking:%s:%s".formatted(tag, rankingType.getValue());
        return this.redisTemplate.opsForZSet().reverseRange(key, start, end);
    }


    public void addAndIncrementScore(String tag, RankingType rankingType, T uId, double averageRevenuePerUser) {
        String key = "ranking:%s:%s".formatted(tag, rankingType.getValue());
        Double oldScore = this.redisTemplate.opsForZSet().score(key, uId);
        if (oldScore != null) {
            double newScore = oldScore + averageRevenuePerUser;
            this.redisTemplate.opsForZSet().add(key, uId, newScore);
        } else {
            this.redisTemplate.opsForZSet().add(key, uId, averageRevenuePerUser);
        }

    }

    public void remove(String tag, RankingType rankingType, T uId) {
        String key = "ranking:%s:%s".formatted(tag, rankingType.getValue());
        this.redisTemplate.opsForZSet().remove(key, uId);
    }

    public void removeAll(String tag, RankingType rankingType) {
        String key = "ranking:%s:%s".formatted(tag, rankingType.getValue());
        this.redisTemplate.delete(key);
    }

    public List<RankInfo<T>> getRankRangeInfo(String tag, RankingType rankingType, int s, int t) {
        String key = "ranking:%s:%s".formatted(tag, rankingType.getValue());
        Long totalSize = this.redisTemplate.opsForZSet().size(key);
        if (totalSize == null || totalSize == 0) {
            return Collections.emptyList();
        }
        return getRankRangeInfo(key, s, t, true);
    }

    public record RankInfo<T>(T id, long rank, double score) {
    }

    public <T> List<RankInfo<T>> getAllRankInfo(String tag, RankingType rankingType) {
        String key = "ranking:%s:%s".formatted(tag, rankingType.getValue());
        Long totalSize = this.redisTemplate.opsForZSet().size(key);
        if (totalSize == null || totalSize == 0) {
            return Collections.emptyList();
        }
        return getRankRangeInfo(key, 0, totalSize - 1, true);
    }

    /**
     * 获取指定排名范围的排名信息
     */
    public <T> List<RankInfo<T>> getRankRangeInfo(String key, long start, long end, boolean reverseOrder) {
        Set<ZSetOperations.TypedTuple<Object>> tuples;
        ZSetOperations<String, Object> zSetOperations = (ZSetOperations<String, Object>) this.redisTemplate.opsForZSet();

        if (reverseOrder) {
            // 逆序（分数从高到低）
            tuples = zSetOperations.reverseRangeWithScores(key, start, end);
        } else {
            // 正序（分数从低到高）
            tuples = zSetOperations.rangeWithScores(key, start, end);
        }

        if (tuples == null) {
            return Collections.emptyList();
        }

        List<RankInfo<T>> result = new ArrayList<>();
        long currentRank = start + 1;

        for (ZSetOperations.TypedTuple<Object> tuple : tuples) {
            @SuppressWarnings("unchecked")
            T id = (T) tuple.getValue();
            result.add(new RankInfo<>(id, currentRank, tuple.getScore()));
            currentRank++;
        }

        return result;
    }

    public Long getRankInfo(String tag, RankingType rankingType, T t) {
        String key = "ranking:%s:%s".formatted(tag, rankingType.getValue());
        ZSetOperations<String, Object> zSetOperations = (ZSetOperations<String, Object>) this.redisTemplate.opsForZSet();
        return zSetOperations.reverseRank(key, t);
    }

}
